#include <sysutil.h>

int
tcp_client(uint16_t port)
{
	int sockfd = 0;
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) {
		ERR_EXIT("tcp_client");
	}
	int on = 1;
	if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,
				(const char *)&on,sizeof(on)) < 0) {
		ERR_EXIT("tcp_client setsockopt");
	}
	struct sockaddr_in conncliaddr;
	conncliaddr.sin_family = AF_INET;
	if(port > 0) {
		conncliaddr.sin_port = htons(port);
	}
	conncliaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	if(bind(sockfd,(struct sockaddr *)&conncliaddr,
						sizeof(conncliaddr)) < 0) {
		ERR_EXIT("bind");
	}
	return sockfd;
}
/**
 * tcp_server - 用来启动一个tcp服务器
 * @host: 服务器的IP地址或者服务器主机名字
 * @port: 服务器端口号
 * 成功返回监听套接字
 */
int
tcp_server_sock_init(const char *host,uint16_t port)
{
	int listenfd = socket(AF_INET,SOCK_STREAM,0);
	if(listenfd < 0) {
		ERR_EXIT("tcp_server_sock_init");
	}
	//进行绑定前的基本操作
	struct sockaddr_in servaddr;
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(port);
	if(NULL != host) {
		if(0 == inet_aton(host,
			&servaddr.sin_addr)) {
			struct hostent *hp;
			hp = gethostbyname(host);
			if(NULL == hp) {
				ERR_EXIT("gethostbyname");
			}
			servaddr.sin_addr = *((struct in_addr *)hp->h_addr);
		}
	} else {
		servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	}
	//设置地址可以重复利用
	int on = 1;
	if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,
		(const char *)&on,sizeof(on)) < 0) {
		ERR_EXIT("setsockopt");
	}
	//进行绑定
	if(bind(listenfd,(struct sockaddr *)&servaddr,
				sizeof(servaddr)) < 0) {
		ERR_EXIT("bind");
	}
	//进行监听
	if(listen(listenfd,SOMAXCONN) < 0) {
		ERR_EXIT("listen");
	}
	return listenfd;
}

#if 0
int
getlocalip(char *ip)
{
	char host[100] = {0};
    if(gethostname(host,sizeof(host)) < 0) {
            ERR_EXIT("gethostname");
    }
	struct hostent *hp;
	if((hp = gethostbyname(host)) == NULL) {
		return  -1;
	}
	//strcpy(ip,inet_ntoa(*(struct in_addr *)hp->h_addr_list[0]));
	strcpy(ip,inet_ntoa(*(struct in_addr *)hp->h_addr));
	return 0;
}
#else
int
getlocalip(char *ip)
{
	struct ifaddrs *ifAddrStruct = NULL;
	void *tmpAddrPtr = NULL;

	getifaddrs(&ifAddrStruct);

	while(NULL != ifAddrStruct) {
		if(AF_INET == ifAddrStruct->ifa_addr->sa_family) {
			tmpAddrPtr = &((struct sockaddr_in *)ifAddrStruct->ifa_addr)->sin_addr;
			char addressBuffer[INET_ADDRSTRLEN];
			inet_ntop(AF_INET,tmpAddrPtr,addressBuffer,INET_ADDRSTRLEN);
			DEBUG_PRINT("%s IP Address %s\n",ifAddrStruct->ifa_name,addressBuffer);
			strcpy(ip,addressBuffer);
		}
		ifAddrStruct = ifAddrStruct->ifa_next;
	}
	return 0;
}
#endif

int
read_timeout(int sockfd,int timeval)
{
	int ret = 0;
	//首先就是判断超时时间
	if(timeval > 0) {
		//首先还是设置文件描述符集合
		fd_set fs;
		FD_ZERO(&fs);
		FD_SET(sockfd,&fs);

		//设定好超时时间
		struct timeval timeout;
		timeout.tv_sec = timeval;
		timeout.tv_usec = 0;
		//调用select进行阻塞
		do {
			ret = select(sockfd + 1,&fs,NULL,NULL,&timeout);
		} while(-1 == ret && EINTR == errno);
		//分情况进行处理
		if(ret < 0) {
			//说明发生了其它的错误
			return ret;
		} else if(0 == ret) {
			//说明超时了，需要进行返回
			errno = ETIMEDOUT;
			ret = -1;
		} else {
			//说明没有超时，有数据到了，需要返回
			ret = 0;
		}
	}
	return ret;
}

int
write_timeout(int sockfd,int tval)
{
	int ret = 0;
	if(tval > 0) {
		fd_set fs;
		FD_ZERO(&fs);
		FD_SET(sockfd,&fs);

		struct timeval timeout;
		timeout.tv_sec = tval;
		timeout.tv_usec = 0;

		do {
			ret = select(sockfd + 1,NULL,&fs,NULL,&timeout);
		} while(-1 == ret && EINTR == errno);
		if(-1 == ret) {
			return ret;
		} else if(0 == ret) {
			errno = ETIMEDOUT;
			ret = -1;
		} else {
			ret = 0;
		}
	}
	return ret;
}

int
accept_timeout(int sockfd,struct sockaddr *addr,
		socklen_t *addrlen,int tval)
{
	int ret = 0;
	if(tval > 0) {
		fd_set fs;
		FD_ZERO(&fs);
		FD_SET(sockfd,&fs);

		struct timeval timeout;
		timeout.tv_sec = tval;
		timeout.tv_usec = 0;

		do {
			//这里是可读文件的描述符的集合
			ret = select(sockfd + 1,&fs,NULL,NULL,&timeout);
		} while(-1 == ret && EINTR == errno);
		if(-1 == ret) {
			return ret;
		} else if(0 == ret) {
			errno = ETIMEDOUT;
			ret = -1;
		}
	}
	//因为这里是监听套接字，当套接字接收到连接后
	//就可以调用accept了
	ret = accept(sockfd,addr,addrlen);
	return ret;
}

void
activate_nonblock(int sockfd)
{
	int ret;
	int flags = fcntl(sockfd,F_GETFL);
	if(-1 == flags) {
		ERR_EXIT("fcntl");
	}
	flags |= O_NONBLOCK;
	ret = fcntl(sockfd,F_SETFL,flags);
	if(-1 == ret) {
		ERR_EXIT("fcntl");
	}
}

void
deactivate_nonblock(int sockfd)
{
	int ret;
	int flags = fcntl(sockfd,F_GETFL);
	if(-1 == flags) {
		ERR_EXIT("fcntl");
	}
	flags &= ~O_NONBLOCK;
	ret = fcntl(sockfd,F_SETFL,flags);
	if(-1 == ret) {
		ERR_EXIT("fcntl");
	}
}

int
connect_timeout(int sockfd,const struct sockaddr *addr,
			socklen_t addrlen,int tval)
{
	int ret = 0;
	if(tval > 0) {
		//这里首先进行connect操作，然后是
		//定时看连接是否超时，如果不超时就可以写了
		//所以不能阻塞到连接成功，
		//而应该通过select来定时判断是否连接成功
		activate_nonblock(sockfd);
	}
	ret = connect(sockfd,(struct sockaddr *)addr,addrlen);
	if(ret < 0 && EINPROGRESS == errno) {
		fd_set connect_fdset;
		struct timeval timeout;
		timeout.tv_sec = tval;
		timeout.tv_usec = 0;
		FD_ZERO(&connect_fdset);
		FD_SET(sockfd,&connect_fdset);
		do {
			ret = select(sockfd + 1,NULL,&connect_fdset,NULL,&timeout);
		} while(ret < 0 && EINTR == errno);
		if(0 == ret) {
			//timeout
			errno = ETIMEDOUT;
			ret = -1;
		} else if(ret < 0) {
			return ret;
		} else {
			//这里返回有两种情况，要么是连接成功，要么连接失败，
			//但是连接失败不会在select中以错误的方式返回
			//所以需要再次使用getsockopt来进行近一步的判断
			int err;
			socklen_t socklen = sizeof(err);
			int sockoptret = getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&err,&socklen);
			if(-1 == sockoptret) {
				return sockoptret;
			}
			if(0 == err) {
				ret = 0;
			} else {
				errno = err;
				ret = -1;
			}
		}
	}
	//要恢复阻塞的状态
	if(tval > 0) {
		deactivate_nonblock(sockfd);
	}
	return ret;
}

ssize_t
readn(int fp,void *buf,size_t count)
{
	int ret = 0;
	//这里按照read调用的方式来封转
	size_t nleft = count;
	ssize_t nread;

	char *bufp = (char *)buf;
	while(nleft > 0) {
		//进行超时判断
		//文件描述符上2048的时候就崩溃了
		if((ret = read_timeout(fp,5)) < 0) {
			//timeout
			return ret;
		}
		if((nread = read(fp,bufp,nleft)) < 0) {
			//小于0有几种情况
			if(errno == EINTR) {//这个是被信号中断不认为错
				continue;
			} 
			//其它错误都直接返回失败
			return nread;
		} else if(0 == nread) {
			return count - nleft;//返回当前读取到的实际数据
		}
		//剩下的情况就是正常读取了数据
		bufp += nread;
		nleft -= nread;
	}
	//返回所有数据
	return count;
}

ssize_t
writen(int fp,const void *buf,size_t count)
{
	int ret = 0;
	//这里按照write的调用方式来封转
	size_t nleft = count;//剩余需要发送的字节数
	ssize_t nwrite;//已经成功发送的字节数

	char *bufp = (char *)buf;
	while(nleft > 0) {
		//进行超时判断
		if((ret = write_timeout(fp,5)) < 0) {
			return ret;
		}
		if((nwrite = write(fp,bufp,nleft)) < 0) {
			if(EINTR == errno) {//这个是被信号中断不认为错
				continue;
			}
			//其它错误都直接返回失败
			return nwrite;
		} else if(0 == nwrite) {//说明写入了0个字节等于什么也没发送
			continue;
		}
		//发送了数据
		bufp += nwrite;
		nleft -= nwrite;
	}
	return count;//必须是完全发送完否则都算错
}

ssize_t
recv_peek(int sockfd,void *buf,size_t len)
{
	int ret;
	while(1) {
		ret = recv(sockfd,buf,len,MSG_PEEK);
		if(-1 == ret && EINTR == errno) {
			continue;
		}
		return ret;
	}
}

ssize_t
readline(int sockfd,void *buf,size_t maxline)
{
	int ret,i;
	int nread;
	char *bufp = (char *)buf;
	int nleft = maxline;
	//只要遇到\n就可以了
	while(1) {
		ret = recv_peek(sockfd,bufp,nleft);
		if(ret < 0) {
			return ret;
		} else if(0 == ret) {
			return ret;//说明对等方可能关闭了
		}
		//printf("echoserv recv_peek ret:%d\n",ret);
		nread = ret;
		for(i = 0;i < nread;++i) {
			if(bufp[i] == '\n') {
				//说明读取到了一行
				//然后这里读取的话，由于设置了MSG_PEEK所以数据
				//并没有移除
				ret = readn(sockfd,bufp,i + 1);
				if(ret != i + 1) {
					DEBUG_PRINT("ret != i + 1\n");
					exit(EXIT_FAILURE);
				}
				return (maxline - nleft) + ret;
			}
		}
		//这里说明没有读取到一行消息，所以需要暂时
		//读出来放到缓冲区当中
		if(nread > nleft) {
			DEBUG_PRINT("nread(%d) > nleft(%d)\n",nread,nleft);
			exit(EXIT_FAILURE);
		}
		nleft -= nread;
		ret = readn(sockfd,bufp,nread);
		if(ret != nread) {//因为已经明确了有nread长度的数据
			DEBUG_PRINT("ret != nread");
			exit(EXIT_FAILURE);
		}
		bufp += nread;//继续去看下一个数据直到有一行数据
	}
	return -1;
}

#define	CONTROLLEN CMSG_LEN(sizeof(int))

int
send_fd(int sockfd,int fd)
{
	struct iovec iov[1];
	struct msghdr msg;
	char buf[2];
	//这个IO缓冲有2字节
	iov[0].iov_base = buf;
	iov[0].iov_len = 2;
	msg.msg_iov = iov;
	//表明这个信息的iov长度为1
	msg.msg_iovlen = 1;
	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	struct cmsghdr *cmptr = NULL;
	if(fd < 0) {
		msg.msg_control = NULL;
		msg.msg_controllen = 0;
		buf[1] = -fd;
		if(0 == buf[1])
			buf[1] = 1;
	} else {
		if(NULL == cmptr && (cmptr = \
			(struct cmsghdr *)malloc(CONTROLLEN)) == NULL) {
			return -1;
		}
		memset(cmptr,0,CONTROLLEN);
		//表明是套接字的方式
		cmptr->cmsg_level = SOL_SOCKET;
		//表明传递的是访问权
		cmptr->cmsg_type = SCM_RIGHTS;
		cmptr->cmsg_len = CONTROLLEN;
		msg.msg_control = cmptr;
		msg.msg_controllen = CONTROLLEN;
		*(int *)CMSG_DATA(cmptr) = fd;
		buf[1] = 0;
	}
	buf[0] = 0;
	//其实msg发送返回的是buf的长度，然后
	//文件描述符其实是在附加数据里面
	if(sendmsg(sockfd,&msg,0) != 2) {
		return -1;
	}
	free(cmptr);
	return 0;
}

#define	MAXLINE 2

int
recv_fd(const int sockfd)
{
	//
	int newfd,nr,status;
	char *ptr;
	char buf[MAXLINE];
	struct iovec iov[1];
	struct msghdr msg;
	struct cmsghdr *cmptr = NULL;
	status = -1;
	for(;;) {
		iov[0].iov_base = buf;
		iov[0].iov_len = sizeof(buf);
		msg.msg_iov = iov;
		msg.msg_iovlen = 1;
		msg.msg_name = NULL;
		msg.msg_namelen = 0;
		if(NULL == cmptr && NULL == \
			(cmptr = (struct cmsghdr *)malloc(CONTROLLEN))) {
			return -1;
		}
		memset(cmptr,0,CONTROLLEN);

		msg.msg_control = cmptr;
		msg.msg_controllen = CONTROLLEN;
		if((nr = recvmsg(sockfd,&msg,0)) < 0) {
			ERR_EXIT("recvmsg");
		} else if(0 == nr) {
			fprintf(stderr,"connection closed by server\n");
			return -1;
		}
		//DEBUG_PRINT(">nr : %d\n",nr);
		//DEBUG_PRINT("[%d %d]\n",buf[0],buf[1]);
		for(ptr = buf;ptr < &buf[nr];) {
			if(*ptr++ == 0) {
				if(ptr != &buf[nr - 1])
					fprintf(stderr,"message format error\n");
				status = *ptr & 0xff;
				if(0 == status) {
					if(CONTROLLEN != msg.msg_controllen) {
						fprintf(stderr,"status = 0 but no fd\n");
						DEBUG_PRINT("msg.msg_controllen %ld CONTROLLEN = %d\n",
												msg.msg_controllen,CONTROLLEN);
					}
					if(NULL == cmptr)
						cmptr = CMSG_FIRSTHDR(&msg);
					if(NULL != cmptr)
						newfd = *(int *)CMSG_DATA(cmptr);
				} else {
					newfd = -status;
				}
				nr -= 2;
			}
		}
		if(NULL != cmptr)
			free(cmptr);
		if(nr > 0)
			return -1;
		if(status >= 0)
			return newfd;
	}
}

inline int 
lock_file_internal(int fd,int lock_type)
{
	int ret;
	struct flock the_lock;
	the_lock.l_type = lock_type;
	the_lock.l_whence = SEEK_SET;
	the_lock.l_start = 0;
	the_lock.l_len = 0;//表示将整个文件加锁
	do {
		ret = fcntl(fd,F_SETLKW,&the_lock);
	} while(ret < 0 && EINTR == errno);//加锁过程不能被信号中断

	return ret;
}

int
lock_file_read(int fd)
{
	return lock_file_internal(fd,F_RDLCK);
}

int
lock_file_write(int fd)
{
	return lock_file_internal(fd,F_WRLCK);
}

int
unlock_file(int fd)
{
	int ret;
	struct flock the_lock;
	the_lock.l_type = F_UNLCK;
	the_lock.l_whence = SEEK_SET;
	the_lock.l_start = 0;
	the_lock.l_len = 0;//表示将整个文件加锁
	//最好不进入阻塞状态
	ret = fcntl(fd,F_SETLK,&the_lock);
	return ret;
}

static struct timeval curr_time;

long
get_time_sec(void)
{
	if(gettimeofday(&curr_time,NULL) < 0) {
		ERR_EXIT("gettimeofday");
	}
	return curr_time.tv_sec;
}

long
get_time_usec(void)
{
	return curr_time.tv_usec;
}

void
nano_sleep(double seconds)
{
	//整数部分获取
	time_t secs = (time_t)seconds;
	//小数部分获取
	double fractional = seconds - (double)secs;

	struct timespec ts;
	ts.tv_sec = seconds;
	ts.tv_nsec = (long)(fractional * 1000000000.0f);
	int ret;
	do {
		ret = nanosleep(&ts,&ts);
	} while(ret < 0 && EINTR == errno);
}

//开启紧急模式
void
activate_oobinline(int fd)
{
	int oob_inline = 1;
	int ret = setsockopt(fd,SOL_SOCKET,SO_OOBINLINE,
					&oob_inline,sizeof(oob_inline));
	if(ret < 0) {
		ERR_EXIT("setsockopt");
	}
}

void activate_sigurg(int fd)
{
	int ret;
	//要让套接字接收到带外数据到来能产生SIGURG信号需要进行如下设置
	ret = fcntl(fd,F_SETOWN,getpid());
	if(ret < 0) {
		ERR_EXIT("fcntl");
	}
}
